package cn.yj.quartz.service;

import cn.yj.annotation.pagehelper.page.Page;
import cn.yj.params.check.annotation.CheckObjectValue;
import cn.yj.params.check.annotation.KeyValue;
import cn.yj.params.check.annotation.Require;
import cn.yj.quartz.QuartzJob;
import cn.yj.quartz.common.ScheduleConst;
import cn.yj.quartz.mapper.QuartzJobMapper;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;

/**
 * 可并发执行
 *
 * @author 永健
 * @since 2022-01-14 17:15
 */
@Service
public class QuartzJobService implements IQuartzJobService {


    @Resource
    private QuartzJobMapper jobMapper;

    @Resource
    private ScheduleExecute scheduleExecute;

    @PostConstruct
    public void initJob() throws SchedulerException {
        ScheduleExecute.build().getScheduler().clear();
        List<QuartzJob> quartzJobs = jobMapper.selectJobAll();
        quartzJobs.forEach(job -> {
            try {
                if (ScheduleConst.Status.NORMAL.getValue().equals(job.getStatus())) {
                    scheduleExecute.createJob(job);
                }
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        });

    }

    @Override
    public Page<QuartzJob> selectJobList(@Require Page<QuartzJob> page, @Require QuartzJob job) {
        jobMapper.selectJobList(page, job);
        return page;
    }


    @Override
    public QuartzJob selectJobById(@Require Long jobId) {
        return jobMapper.selectJobById(jobId);
    }

    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    @Override
    public int deleteJobById(@Require Long jobId) throws SchedulerException {
        QuartzJob quartzJob = selectJobById(jobId);
        int row = jobMapper.deleteJobById(jobId);
        if (row > 0) {
            ScheduleExecute.build().deleteJob(quartzJob);
        }
        return row;
    }

    @Transactional
    @Override
    public void deleteJobByIds(@Require Long[] ids) throws SchedulerException {
        for (int i = 0; i < ids.length; i++) {
            deleteJobById(ids[i]);
        }
    }

    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    @Override
    @CheckObjectValue(keyValue = @KeyValue(type = QuartzJob.class, name = {"jobId", "jobName", "jobGroup",
            "invokeTarget", "cronExpression", "misfirePolicy", "concurrent", "status"}))
    public int updateJob(QuartzJob job) throws SchedulerException {
        QuartzJob quartzJob = selectJobById(job.getJobId());
        int row = jobMapper.updateJob(job);

        // 判断是否存在
        quartzJob.setJobGroup(job.getJobGroup());
        JobKey jobKey = ScheduleExecute.build().createJobKey(quartzJob);
        if (ScheduleExecute.build().getScheduler().checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            ScheduleExecute.build().getScheduler().deleteJob(jobKey);
        }

        if (ScheduleConst.Status.NORMAL.getValue().equals(job.getStatus())) {
            ScheduleExecute.build().createJob(job);
        }
        return row;
    }

    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    @Override
    @CheckObjectValue(keyValue = @KeyValue(type = QuartzJob.class, name = {"jobName", "jobGroup", "invokeTarget",
            "cronExpression", "misfirePolicy", "concurrent", "status"}))
    public int insertJob(QuartzJob job) throws SchedulerException {
        int rows = jobMapper.insertJob(job);
        if (rows > 0) {
            ScheduleExecute.build().createJob(job);
        }
        return rows;
    }

    /**
     * 立即运行任务
     *
     * @param jobId
     */
    @Override
    public boolean runJob(@Require Long jobId) throws SchedulerException {
        QuartzJob job = selectJobById(jobId);
        // 参数
        JobKey jobKey = ScheduleExecute.build().createJobKey(job);
        if (!ScheduleExecute.build().getScheduler().checkExists(jobKey)){
            job.setMisfirePolicy(ScheduleConst.MISFIRE_FIRE_AND_PROCEED);
            ScheduleExecute.build().createJob(job);
            return true;
        }
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConst.TASK_PROPERTIES, job);
        ScheduleExecute.build().getScheduler().triggerJob(jobKey, dataMap);
        return true;
    }

    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    @Override
    public int resumeJob(@Require Long jobId) throws SchedulerException {
        QuartzJob job = selectJobById(jobId);
        job.setStatus(ScheduleConst.Status.NORMAL.getValue());
        int rows = jobMapper.updateJob(job);
        if (rows > 0) {
            ScheduleExecute.build().getScheduler().resumeJob(ScheduleExecute.build().createJobKey(job));
        }
        return rows;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Exception.class})
    public boolean pauseJob(@Require Long jobId) throws SchedulerException {
        QuartzJob job = selectJobById(jobId);
        job.setStatus(ScheduleConst.Status.PAUSE.getValue());
        if (jobMapper.updateJob(job) > 0) {
            scheduleExecute.pauseJob(job);
        }
        return true;
    }
}
